/**
 * File: GatherQualityStatistics.java
 * Created by: mhaimel
 * Created on: Nov 25, 2009
 * CVS:  $Id: GatherQualityStatistics.java,v 1.1 2009/12/07 09:45:36 mhaimel Exp $
 */
package uk.ac.ebi.curtain.exec;

import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javolution.util.FastList;

import org.apache.commons.lang.NotImplementedException;

import uk.ac.ebi.curtain.utils.StatisticHelper;
import uk.ac.ebi.curtain.utils.data.FileType;
import uk.ac.ebi.curtain.utils.data.ReadType;
import uk.ac.ebi.curtain.utils.file.EntryMapper;
import uk.ac.ebi.curtain.utils.file.EntryMapperBean;
import uk.ac.ebi.curtain.utils.file.FileInfo;
import uk.co.flamingpenguin.jewel.cli.CliFactory;
import uk.co.flamingpenguin.jewel.cli.Option;
import uk.co.flamingpenguin.jewel.cli.Unparsed;

/**
 * @author mhaimel
 *
 */
public class GatherQualityStatistics {
	private List<File> fList = new LinkedList<File>();
	private ScoreType method = ScoreType.Phred; // Phred score DEFAULT
	
	public GatherQualityStatistics() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		GatherQualityStatistics gq = new GatherQualityStatistics();
		try{
			GatherQualityScope param = CliFactory.parseArguments(GatherQualityScope.class, args);
			if(param.isOutput()){
				// TODO
				throw new NotImplementedException();
			}
			if(!param.isFiles()){
				String msg = "No files specified!";
				System.err.println(msg);
				throw new IllegalArgumentException(msg);
			}
			if(param.isMethod()){
				gq.method = param.getMethod();
			}
			for(File f : param.getFiles()){
				if(!f.exists()){
					String msg = "File does not exist: "+ f;
					System.err.println(msg);
					throw new IllegalArgumentException(msg);
				}
				gq.addFile(f);
			}
			gq.process();
		} catch (Exception e) {
			e.printStackTrace(System.err);
			System.exit(1);
		}
		System.exit(0);
	}

	private void process() {
		for(File f : fList){
			process(f);
		}
	}

	private void process(File f) {
		FileInfo fi = wrap(f);
		final QualityCollection data = new QualityCollection();
		final AtomicInteger cnt = new AtomicInteger(0);
		try{
			fi.getFileType().getParser().parse(fi, new EntryMapper() {
				
				@Override
				public void mapEntry(EntryMapperBean bean) {
					if(cnt.incrementAndGet()%1234 == 0){
//						throw new RuntimeException();
						update(data, bean);
					}
				}
			});
		} catch (RuntimeException e) {
			System.out.println("got it... " + cnt.get());
		}
		PrintStream out = getOutput();
		System.out.println("File: " + f);
		out.println("pos\tmin\tmean\tq1\tmedian\tq3\t");
		
		for(int i = 0; i < data.data.size(); ++i){
			List<Integer> val = data.data.get(i);
			Collections.sort(val);	
			out.print(i);
			out.print("\t");
			out.print(val.get(0));
			out.print("\t");
			out.print(StatisticHelper.mean(val));
			out.print("\t");
			out.print(StatisticHelper.q1(val));
			out.print("\t");
			out.print(StatisticHelper.median(val));
			out.print("\t");
			out.print(StatisticHelper.q3(val));
			out.println("\t");
		}
		System.out.println("\n*******************************************\n");
	}
	
	
	private PrintStream getOutput(){
		return System.out;
	}
	
	private void update(QualityCollection data, EntryMapperBean bean){
		String qual = bean.getQuality().trim();
		int len = qual.length();
		if(data.data.isEmpty()){
			data.init(len);
		}
		for(int i = 0; i < len; ++i){
			data.data.get(i).add(getScore(qual.charAt(i)));
//			Integer val = ((int)qual.charAt(i))-33;
//			Double val = 10 * Math.log(1 + Math.pow(10,(((int)qual.charAt(i)-64) / 10.0))) / Math.log(10);
			// phred ((int)qual.charAt(i))-33;
//			 Double d = 10 * Math.log(1 + Math.pow(10,(((int)qual.charAt(i)-64) / 10.0))) / Math.log(10);
			// 10 * log(1 + 10 ** (ord($sq) - 64) / 10.0)) / log(10)
		}
	}
	
	private int getScore(char c){
		if(ScoreType.Phred.equals(method)){
			return ((int)c)-33;
		} else if(ScoreType.Solexa.equals(method)){
			Double d = 10 * Math.log(1 + Math.pow(10,(((int)c-64) / 10.0))) / Math.log(10);
			return d.intValue();
		}
		throw new NotImplementedException("Method not implemented: " + method);
	}
	
	private static class QualityCollection {
		private List<List<Integer>> data = new ArrayList<List<Integer>>();

		public void init(int len) {
			for(int i = 0; i < len; ++i){
				data.add(new FastList<Integer>());
			}
		}
		
		
		
	}

	private FileInfo wrap(File f) {
		return new FileInfo(f,FileType.fastq,ReadType.Contig);
	}

	private void addFile(File f) {
		fList.add(f);
	}
	
	private static enum ScoreType{
		Phred,
		Solexa;
		
	}
	
	private static interface GatherQualityScope {
		
		@Option(shortName = "o")
		public File getOutput();
		
		public boolean isOutput();
		
		@Option(shortName = "m")
		public ScoreType getMethod();
		
		public boolean isMethod();
		
		@Unparsed
		public List<File> getFiles();
		
		public boolean isFiles();
		
	}
}
